|† Home Page †|† Source Code Index †|† Downloads †|† Email †|

property version 2.1

Released 24/6/96

This page last updated 30/6/96

This is the latest version of property. The major changes are that property now takes two parameters when instantiating a template, and the constructors now have differing numbers of parameters, depending on the access permission given. This tells users of the class what access is allowed to a property, and allows simpler (and more robust) constructors. Additionally, properties can now be converted from read-write to read-only or write-only (but not in any other direction).

What is a property ?

Why use properties ?

What are the disadvantages when using properties ?

How do I use the property template class ?

Limitations of the property template class implementation

Problems with the property template class implementation

Revision History

Legal Stuff

Download property 2.1 (binhex)
Download property 2.1 (PKZip)


What is a property ?

A property is a data type with separate reading and writing access methods. It is a highly abstract entity whose implementation is completely hidden from the user. A property is designed to act a closely as possible as though it were simply a data member of the base type for the property.

The access methods for a property are either direct access to a data member (aliasing) or member function calling. A property may have different methods for reading and writing. A property may be read-only (no access method for writing), write-only (no access method for reading) or read-write (access methods are defined for both reading and writing).

The most important thing about properties is that when using a property it is not possible to determine whether the property is aliasing a data member or calling a member function. A property incorporates its access methods, but the interface abstracts them.


Why use properties ?

So what's the big deal about using properties ? After all, you can get pretty much all the functionality described above with the use of data members and accessor functions. It boils down to two things: abstractness and personal choice.

Personal Choice

It's simple. I hate assigning something to a function. When I see code like

    Left() = 0;

I just cringe. Of course, you can always use a setter function

    SetLeft(0);

which feels nicer, but just doesn't have the simplicity of

    Left = 0;

So why not just make Left a data member ? Quite simply, I abhor exposing data members in the public section of a class declaration. I don't even like doing it in the protected section. It's just asking for trouble if you ask me - what happens (for example) if you decide that you need to change it to an accessor function ? All the code which uses that data member needs to be changed. Which bring us to the next point:

Abstractness

Properties are very abstract (have I mentioned that yet ?). The person using the property has no idea if it is calling a member function or directly accessing a data member. It's not important. What is important is that by setting a property to a particular value the object will end up in a state consistent with the new value. Conceptually properties correspond to state: setting the state of an object (may) cause something to happen. This is the exact opposite of getter and setter functions, where doing something (may) cause the state of the object to change.

The two approaches are not mutually exclusive - for some problems one way works best, for other problems it doesn't matter. Properties simply model one conceptual view of a system.


What are the disadvantages when using properties ?

Properties trade off size and speed for abstractness. A property will be larger than the type it is modelling. A property must hold information about the access methods for the property. As well, since a property incorporates its access methods, the memory associated with these access methods must be included in the size of the property. Usually this will involve a data member of the base type and possibly some member functions. As well, since there is at least one added level of indirection there is a corresponding increase in execution time.


How do I use the property template class ?

In the class header declare a property of the wanted base class. The template declaration for a property (declared in "property.h") is

    enum EPropertyAccess
    {
        propertyAccess_ReadOnly    = 'PAR0',
        propertyAccess_WriteOnly   = 'PA0W',
        propertyAccess_ReadWrite   = 'PARW'
    };

    template <EPropertyAccess access, class T>    class property
    {
        ...
    };

The accessor function(s) and/or data member(s) associated with the property must also be declared in the class header. A data member must be of the same type as the base type of the property. Getter and setter functions must be static, friend or external functions declared as types

    void (*getFunc) (void * const, T & )

and the setter function of type

    void (*setFunc) (void * const, const T & )

respectively.

    #include "property.h"

    class TestProperty
    {
        public:

                            TestProperty (void  );

            property<propertyAccess_ReadWrite,
                     short> pReadWrite;

            property<propertyAccess_ReadOnly,
                     short> pReadOnly;

            property<propertyAccess_WriteOnly,
                     short> pWriteOnly;

        protected:

            short           mReadWrite,
                            mReadOnly,
                            mWriteOnly;

            static void     GetReadWrite (void * const  theObject,
                                          short         &oldReadWrite   );
            static void     SetReadWrite (void * const  theObject,
                                          const short   &newReadWrite   );
 
            static void     GetReadOnly (void * const  theObject,
                                         short         &oldReadOnly     );
 
            static void     SetWriteOnly (void * const  theObject,
                                          const short   &newWriteOnly   );
   };

The property must be initialised in the constructor initialisation list. The constructor for a property takes either 2 or 3 parameters, depending on the first parameter to the template (of type EPropertyAccess).

Using the wrong constructor format will result in an exception being thrown from the constructor of the property.

The following are valid constructors for the above class declaration.

//  pReadWrite aliases mReadWrite for both reading and writing
//  pReadOnly aliases mReadOnly for reading
//  pWriteOnly aliases mWriteOnly for writing
    
    TestProperty::TestProperty (void    )
    :   pReadWrite(this, mReadWrite, mReadWrite)
        pReadOnly(this, mReadOnly),
        pWriteOnly(this, mWriteOnly)
    {
    }
//  pReadWrite calls GetReadWrite for reading, and aliases mReadWrite for writing
//  pReadOnly calls GetReadOnly for reading
//  pWriteOnly aliases mWriteOnly for writing
    
    TestProperty::TestProperty (void    )
    :   pReadWrite(this, GetReadWrite, mReadWrite)
        pReadOnly(this, GetReadOnly),
        pWriteOnly(this, mWriteOnly)
    {
    }
//  pReadWrite aliases mReadWrite for reading, and calls SetReadWrite for writing
//  pReadOnly aliases mReadOnly for reading
//  pWriteOnly calls SetWriteOnly for writing
    
    TestProperty::TestProperty (void    )
    :   pReadWrite(this, mReadWrite, SetReadWrite)
        pReadOnly(this, mReadOnly),
        pWriteOnly(this, SetWriteOnly)
    {
    }
//  pReadWrite calls GetReadWrite for reading and SetReadWrite for writing
//  pReadOnly calls mReadOnly for reading
//  pWriteOnly calls SetWriteOnly for writing
    
    TestProperty::TestProperty (void    )
    :   pReadWrite(this, GetReadWrite, SetReadWrite)
        pReadOnly(this, GetReadOnly),
        pWriteOnly(this, SetWriteOnly)
    {
    }

In general, properties may be assigned to and read from just as though they were normal data members of the base type. A property may be safely passed to a function as either a value or constant reference parameter of the base type of the property. See the limitations on the use of properties.

Note: Copying a property and assigning one property to another will have different effects. In those circumstances where the copy constructor is called, such as when a property is passed by value to a function (where the parameter is actually of the property type, not the base type), an identical copy of the original property will be made - the copy will have the same access methods as the original. In circumstances where the assignment operator is used the value of the first property will be assigned to the second property, calling the read method of the first property and the write method of the second.

There is one additional situation: where a copy of a property is made with different access permission. This can occur in the following situation:

    void   ChangeAccess (property<propertyAccess_ReadOnly, short>  p )
    {
        short  i = p;
    }

    int main (void )
    {
        TestProperty   t;

        ChangeAccess(t.pReadOnly);
        ChangeAccess(t.pReadWrite);
    }

When creating a copy of a property in this manner the following access permission conversions are allowed:

Attempting to perform any other access permission changes on a property will result in an exception at runtime.


Limitations of the property template class implementation

Passing a property as a non-constant reference parameter of a function will result in a temporary unnamed variable being created.

A pointer to a property may not be passed as a parameter to a function where the function is expecting a pointer to the base type of the property. A pointer to the property may not be cast to a pointer to the base type.

Reading from a write-only property and writing to a read-only property cannot be detected at compile-time. An exception will be thrown at runtime if this situation occurs.

A property with read-write access may not yet be converted to one with either read-only or write-only access through the copy constructor. Hence a read-write property cannot be passed to a function expecting a read-only or write-only property. This will be addressed in a future version.

An implicit conversion to the base type will be performed when assigning the value of the property to a variable of the base type. The conversion functions of the base type will not be used unless an explicit cast is made. Thus a property may only be assigned to a variable of its base type.

A type cannot be used as a base type for a property unless it can be assigned to and read from.

"property.h" must be included into the source file after all classes which are to be used as base types for properties. This is due to the previous limitation: until the declaration for the base type is known it is impossible for a template to be instantiated which will allow assignment and conversion.

A property cannot pass a non-static member function as the getter or setter method. Either a static, friend or external function must be passed. A pointer to the object will be passed as the first parameter. This is of type void * const and must be cast to a pointer to the class before it can be used. There are two simple ways of dealing with this: using the object passed directly, or passing it to a member function to deal with. The second is the recommended way, and is demonstrated in the following declaration. Note: this method also allows subclasses to override the getter and setter functions which actually do the work.

    class TestProperty
    {
        public:
    
                            TestProperty (void  );
    
            property<short> pShort;
    
        protected:
    
            short           mShort;
    
            virtual void    GetShort (short         &oldShort       );
            virtual void    SetShort (const short   &newShort       );

        private:

            static void     GetShort (void * const  theObject,
                                      short         &oldShort   )
                            {
                                ((TestProperty *) theObject)->GetShort(oldShort);
                            }

            static void     SetShort (void * const  theObject,
                                      const short   &newShort   )
                            {
                                ((TestProperty *) theObject)->SetShort(newShort);
                            }
    };

    TestProperty::TestProperty (void    )
    :   pShort(this, GetShort, SetShort)
    {
    }


Problems with the property template class implementation

None known.


Revision History

2.1

A property may now have its access permissions changed when copying the property. An unsafe change (read-only to read-write, read-only to write-only, write-only to read-only or write-only to read-write) will result in an exception at runtime.

2.0

The property template now takes access permission for the property.

The constructors now take differing numbers of parameters. If the constructor invoked does not match the access permission specified an exception is thrown.

1.0

First non-beta release (I'm pretty sure there aren't any bugs in this).

No longer invokes undefined behaviour in the constructors. I checked up at the Online C++ FAQs web site (maintained by Marshall Cline, cline@parashift.com) and discovered that I was right - I couldn't do what I was doing with references. However, the fix was easy - I was already assigning the address of the reference to the union. Since a reference aliases a variable it is valid to take that address and use it - you have taken the address of the actual variable. I thus changed mGet.mGetField and mSet.mSetField to type T * rather than type T & and assigned the addresses of the getField and setField reference parameters to them. Then to access the field internally I simply dereference the pointers.

This is a good example where pointers are used internally, hidden from the external interface. The user of the class only sees references.

1.0b2

Invokes undefined behaviour (resetting references). Do not use this version.

Getter and setter functions are now type-safe. The constructors accept function pointers of the types

    void (*) (void *, T &)

and

    void (*) (void *, const T &)

1.0b1

First public release. Using getter and setter functions is not type-safe: the constructors accept function pointers of the types

    void (*) (void *, void *)

and

    void (*) (void *, const void *)


Legal Stuff

property is © 1996 Boxes Objects Links Design Pty Ltd. All Rights Reserved. You use this software at your own risk, etc. Permission is given to Timothy C. Delaney to use property. Permission is given for all others to use property if acknowledgement of this copyright is given in publically-released software. Acknowledgement should consist of a statement equivalent to "Sections of this program are © Boxes Objects Links Design Pty Ltd", visible in an "About..." box or splash screen.


|† Home Page †|† Source Code Index †|† Downloads †|† Email †|